package com.powergisol.core.math;

import com.powergisol.core.analysis.SpatialAnalysis;

import java.util.Objects;

public class Line {
    /**
     * 直线经过的1点
     */
    private Dot m;

    /**
     * 直线经过的另一点
     */
    private Dot n;
    /**
     * 方向向量
     */
    private Vector directionVector;

    /**
     * 如果endpointA 和 endpointB 同时不为空说明这是一个线段,否则为射线或者直线
     */
    private Dot endpointA ;


    private Dot endpointB;


    public Line(Dot m,Dot n){
        this.m = m;
        this.n = n;
        this.directionVector = new Vector(m,n);
    }

    public Line(Dot m,Vector directionVector){
        this.m = m;
        this.directionVector = directionVector;
    }


    public Line(Dot m,Dot n,boolean isSegment){
        this.m = m;
        this.n = n;
        this.directionVector = new Vector(m,n);

        if(isSegment){
            this.endpointA = m;
            this.endpointB = n;
        }
    }


    public void setEndpointA(Dot endpointA) {
        this.endpointA = endpointA;
    }

    public Dot getEndpointA() {
        return endpointA;
    }

    public void setEndpointB(Dot endpointB) {
        this.endpointB = endpointB;
    }

    public Dot getEndpointB() {
        return endpointB;
    }

    /**
     * 求空间中直线和直线的交点
     * @param line
     * @return 如果
     */
    public Dot getCrossPoint(Line line) {
        /**
         * 当前直线为L1
         */

        Vector directionVector = line.getDirectionVector();
        //求两个直线的方向向量的发向量
        Vector vector = this.directionVector.crossProduct(directionVector);
        //一直线经过的点 再和 两条线的方向量构成法线
        Dot m = line.getM();
        Dot n = this.getM();
        //从两条线上各取1点,形成直线,如果这条直线的方向向量也垂直于向量vector,说明两直线在一个平面上
        Line lineMN = new Line(m,n);
        //如果不在一个平面上,返回空
        if (lineMN.getDirectionVector().dotProduct(vector)!=0){
            return null;
        }
        /**
         * 经过点m 方向向量为vector 的直线newLine
         */
        Line newLine = new Line(m,vector);

        /**
         * newLine 和line 构成1个新的面plane，问题又变成了这个面和直线L1本身的交点问题
         * newLine和line的法向量
         */

        double v1, v2, v3;// 直线L1方向向量
        double m1, m2, m3; //直线L1经过的1点
        double n1, n2, n3; //平面plane经过点n
        double vp1, vp2, vp3; //plane法向量vp

        m1 = this.getM().getX();
        m2 = this.getM().getY();
        m3 = this.getM().getZ();
        v1 = this.getDirectionVector().getX();
        v2 = this.getDirectionVector().getY();
        v3 = this.getDirectionVector().getZ();

        n1 = m.getX();
        n2 = m.getY();
        n3 = m.getZ();

        Vector crossProduct = newLine.getDirectionVector().crossProduct(lineMN.getDirectionVector());
        vp1 = crossProduct.getX();
        vp2 = crossProduct.getY();
        vp3 = crossProduct.getZ();
        /**
         * t 是通过平面点法式带入 直线的参数里面得到
         */
        double v = vp1 * v1 + vp2 * v2 + vp3 * v3;
        //如果平行
        if(v ==0){
            return null;
        }
        double t = ((n1 - m1) * vp1 + (n2 - m2) * vp2 + (n3 - m3) * vp3) / v ;
        double x = m1+ v1 * t;
        double y = m2+ v2 * t;
        double z = m3+ v3 * t;

        return new Dot(x,y,z);

    }


    /**
     * 判断是否在直线之上
     * @param dot
     * @return
     */
    public boolean contains(Dot dot){
        /**
         * 定义m到点的向量
         */
        Vector mc = new Vector(m,dot);
        boolean parallal = directionVector.parallalTo(mc);

        //if(!parallal){
            /**
             * 定义n到点的向量,注意这里实际是定义的点到n的向量,因为点乘是算的cnm的夹角需要向量nc和向量nm,这里算的是cn和mn的实际是负负得正
             */
            Vector nc = new Vector(dot,n);

            double cmn = directionVector.dotProduct(mc);
            double cnm = directionVector.dotProduct(nc);
            /**
             * 如果两个夹角都是锐角，则点在直线上
             */
            if(cmn>=0 && cnm>=0){
                return true;
            }
        //}

        return false;
    }

    public boolean containsWithXY(Dot dot){

        double xMax = SpatialAnalysis.compute(Math.max(m.getX(), n.getX()),8);
        double xMin = SpatialAnalysis.compute(Math.min(m.getX(), n.getX()),8);
        double yMax = SpatialAnalysis.compute(Math.max(m.getY(), n.getY()),8);
        double yMin = SpatialAnalysis.compute(Math.min(m.getY(), n.getY()),8);
        double zMax = SpatialAnalysis.compute(Math.max(m.getZ(), n.getZ()),8);
        double zMin = SpatialAnalysis.compute(Math.min(m.getZ(), n.getZ()),8);

        double x = SpatialAnalysis.compute(dot.getX(),8);
        double y = SpatialAnalysis.compute(dot.getY(),8);
        double z = SpatialAnalysis.compute(dot.getZ(),8);

        if(x<xMin||x>xMax){
            return false;
        }

        if(y<yMin||y>yMax){
            return false;
        }

        //if(z<zMin||z>zMax){
         //   return false;
        //}

        return true;
    }

    /**
     * 直线定义,经过一个点,一个方向向量,可以确定一条直线
     * @param o
     * @return
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Line line = (Line) o;

        if (!Objects.equals(m, line.m)) return false;
        if (!Objects.equals(n, line.n)) return false;
        return Objects.equals(directionVector, line.directionVector);
    }

    @Override
    public int hashCode() {
        int result = m != null ? m.hashCode() : 0;
        result = 31 * result + (n != null ? n.hashCode() : 0);
        result = 31 * result + (directionVector != null ? directionVector.hashCode() : 0);
        return result;
    }

    public Dot getM() {
        return m;
    }

    public Dot getN() {
        return n;
    }

    public Vector getDirectionVector() {
        return directionVector;
    }
}
